package com.jianyushe.gof23.learncode.creationalpattern.singleton;

import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.concurrent.CountDownLatch;


/**
 * @author Create by Alvin
 * @Description 单利模式
 * @date 2021-10-09 11:28
 */
public class SingleTonTest {
    private static final Logger SINGLETON_LOGGER = LoggerFactory.getLogger(SingleTonTest.class);

    /**
     * 饿汉模式
     */
    static class HungryInstance {
        private static final Logger LOGGER = LoggerFactory.getLogger(HungryInstance.class);
        // 饿汉模式  即时加载(没有延时加载（懒加载）优势) 如果没被调用则浪费资源
        // warning原因 是因为new 对应的Class中 只包含了静态变量，静态方法，idea认为你没有必要去new 出来一个对象，所以进行了提示。
        // 解决方法 只要在对象中加入一些实例变量和方法就可以了。
        private static final HungryInstance INSTANCEOFHUN = new HungryInstance();

        HungryInstance() {
            // 防反射破解INSTANCEOFHUN != null
            if (INSTANCEOFHUN != null) {
                throw new RuntimeException("");
            }
            LOGGER.info("单利饿汉模式创建对象...");
        }

        // 方法没同步，调用效率高
        public static HungryInstance getHungryInstance() {
            return INSTANCEOFHUN;
        }

        // 防反序列化破解
        private Object readResolve() {
            return INSTANCEOFHUN;
        }
    }

    /**
     * 懒汉模式
     */
    static class LazyInstance {
        private static final Logger LAZY_LOGGER = LoggerFactory.getLogger(LazyInstance.class);
        // 不初始化延时加载（懒加载）
        private static LazyInstance lazyInstance;

        private LazyInstance() {
            LAZY_LOGGER.info("懒汉模式创建对象...");
        }

        // 可能同时会有线程访问，造成创建多个对象的情况  所以要加锁   造成调用效率低
        public static synchronized LazyInstance getLazyInstance() {
            if (lazyInstance == null) {
                lazyInstance = new LazyInstance();
            }
            return lazyInstance;
        }
    }

    /**
     * 还存在一种方式   双端检锁模式， 但基本上不会用，
     */

    /**
     * 静态内部类  是比较好的方式， 延时加载
     * 且不会出现线程安全问题，不用加sync 也就不存在等待锁释放
     */
    static class StaticInnerInstance {
        private static final Logger INNER_LOGGER = LoggerFactory.getLogger(StaticInnerInstance.class);

        private static class SingleTonInnerInstance {
            private static final StaticInnerInstance STATIC_INNER_INSTANCE = new StaticInnerInstance();
        }

        StaticInnerInstance() {
            INNER_LOGGER.info("单例模式-静态内部类创建...");
        }

        public static StaticInnerInstance getStaticInnerInstance() {
            return SingleTonInnerInstance.STATIC_INNER_INSTANCE;
        }
    }

    /**
     * 单例模式就是常量对象， 枚举刚好可以定义常量对象
     */
    enum EnumInstance {
        ENUM_INSTANCE;

        /**
         * 操自定义作的方法
         */
        public void getInstace() {
        }
    }

    @Test
    public void test() {
        // 饿汉模式
        HungryInstance hungryInstance = HungryInstance.getHungryInstance();
        HungryInstance hungryInstance2 = HungryInstance.getHungryInstance();
        SINGLETON_LOGGER.debug("hungryInstance == hungryInstance2   >>>>>>>  {}", (hungryInstance == hungryInstance2));

        // 懒汉模式
        LazyInstance lazyInstance = LazyInstance.getLazyInstance();
        LazyInstance lazyInstance2 = LazyInstance.getLazyInstance();
        SINGLETON_LOGGER.debug("lazyInstance == lazyInstance2   >>>>>>>  {}", (lazyInstance == lazyInstance2));

        // 静态内部类模式
        StaticInnerInstance staticInnerInstance = StaticInnerInstance.getStaticInnerInstance();
        StaticInnerInstance staticInnerInstance2 = StaticInnerInstance.getStaticInnerInstance();
        SINGLETON_LOGGER.debug("staticInnerInstance == staticInnerInstance2   >>>>>>>  {}", (staticInnerInstance == staticInnerInstance2));

        // 还有一种枚举的方式  枚举天然的单利   不需要延时加载的话  枚举好于饿汉模式

    }

    /**
     * 多线程测试效率
     */
    @Test
    public void multithreadedTest() throws InterruptedException {
        long start = System.currentTimeMillis();
        int threadNum = 10;
        int instanceNum = 1000000;
        // 多线程时使用这种方式等待所有线程执行完成
        CountDownLatch countDownLatch = new CountDownLatch(threadNum);
        for (int i = 0; i < threadNum; i++) {
            new Thread(() -> {
                for (int k = 0; k < instanceNum; k++) {
                   // HungryInstance.getHungryInstance();
                   // LazyInstance.getLazyInstance();
                   // StaticInnerInstance.getStaticInnerInstance();
                    EnumInstance.ENUM_INSTANCE.getInstace();
                }
                // 一个线程执行完成，则减少一个
                countDownLatch.countDown();
            }).start();
        }
        // 等待所有线程执行完成
        countDownLatch.await();
        long end = System.currentTimeMillis();
        SINGLETON_LOGGER.info("总耗时：{}", end - start);
    }

}
